Make sure native W32 print dialog uses visual styles
authorРуслан Ижбулатов <lrn1986@gmail.com>
Tue, 29 Jul 2014 08:18:32 +0000 (08:18 +0000)
committerРуслан Ижбулатов <lrn1986@gmail.com>
Tue, 5 Aug 2014 06:41:31 +0000 (06:41 +0000)
For that to happen the libgtk3 is embedded with a manifest that requests
common controls library 6.x, and GTK lazily calls InitCommonControlsEx()
to initialize those. Then this manifest is used to temporarily override
the process activation contest when loading comdlg32 (which contains the
code for the print dialog), ensuring that it too depends on common
controls 6.x, even if the application that uses GTK does not.

https://bugzilla.gnome.org/show_bug.cgi?id=733773

configure.ac
gtk/Makefile.am
gtk/gtk-win32.rc.in
gtk/gtkprintoperation-win32.c
gtk/gtkprivate.h
gtk/gtkwin32.c
gtk/libgtk3.manifest.in [new file with mode: 0644]

index 891094db72da22e2eb29f5a544fb556239861da6..314dacf82b43ad7936cd6709748e6a80e92ed607 100644 (file)
@@ -1894,6 +1894,7 @@ gtk/Makefile
 gtk/makefile.msc
 gtk/gtkversion.h
 gtk/gtk-win32.rc
+gtk/libgtk3.manifest
 gtk/inspector/Makefile
 gtk/native/Makefile
 util/Makefile
index 2f74d2e862bff5fd71cb06aa37657f635068fb51..5c31084a7ac565a8783916eeedd576427e63dcad 100644 (file)
@@ -90,7 +90,7 @@ gtk_win32_symbols = -export-symbols $(srcdir)/gtk.def
 gtk_win32_res = gtk-win32-res.o
 gtk_win32_res_ldflag = -Wl,gtk-win32-res.o
 
-gtk-win32-res.o : gtk-win32.rc
+gtk-win32-res.o : gtk-win32.rc libgtk3.manifest
        $(WINDRES) gtk-win32.rc $@
 
 gtk.def: libgtk-3.la
@@ -1726,6 +1726,7 @@ EXTRA_DIST +=                   \
        $(adwaita_sources)      \
        $(win32_theme_sources)  \
        $(gsettings_SCHEMAS)    \
+       libgtk3.manifest.in     \
        gtk-win32.rc.in         \
        gtkwin32embed.h         \
        gtkwin32embedwidget.h   \
index 4a42d5a666097222a8c5f11bb508d9ac1f12b912..dc9464a2c1a1b9d5fbdc1e2efbbadd4d1d099b56 100644 (file)
@@ -28,3 +28,4 @@ VS_VERSION_INFO VERSIONINFO
       VALUE "Translation", 0x409, 1200
     END
   END
+ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST libgtk3.manifest
index e59eb9f6758e36e37c04b0896b5d4310a3688fb1..3ac6bc47bfe5b357af5be3335eb46c892e6d52e1 100644 (file)
@@ -1657,6 +1657,25 @@ gtk_print_operation_run_with_dialog (GtkPrintOperation *op,
   GtkPrintOperationPrivate *priv;
   IPrintDialogCallback *callback;
   HPROPSHEETPAGE prop_page;
+  static volatile gsize common_controls_initialized = 0;
+
+  if (g_once_init_enter (&common_controls_initialized))
+    {
+      BOOL initialized;
+      INITCOMMONCONTROLSEX icc;
+
+      memset (&icc, 0, sizeof (icc));
+      icc.dwSize = sizeof (icc);
+      icc.dwICC = ICC_WIN95_CLASSES;
+
+      initialized = InitCommonControlsEx (&icc);
+      if (!initialized)
+        g_warning ("Failed to InitCommonControlsEx: %lu\n", GetLastError ());
+
+      _gtk_load_dll_with_libgtk3_manifest ("comdlg32.dll");
+
+      g_once_init_leave (&common_controls_initialized, initialized ? 1 : 0);
+    }
   
   *do_print = FALSE;
 
index a07f7292ba1bf47fcff35d6b7695506bb67265b5..5fe7d88eaee8cab6f3250db422b6e44df9ad058e 100644 (file)
@@ -91,6 +91,10 @@ gboolean        _gtk_propagate_captured_event  (GtkWidget       *widget,
                                                 GdkEvent        *event,
                                                 GtkWidget       *topmost);
 
+#ifdef G_OS_WIN32
+void _gtk_load_dll_with_libgtk3_manifest (const char *dllname);
+#endif
+
 G_END_DECLS
 
 #endif /* __GTK_PRIVATE_H__ */
index e8bd7ef133b9736dbd9e2a75293e79c65e632f14..c33659e27847b25f225c1cc02654bc024d451e5d 100644 (file)
 
 #define STRICT
 #include <windows.h>
+#include <commctrl.h>
 #undef STRICT
 
+/* In practice, resulting DLL will have manifest resource under index 2.
+ * Fall back to that value if we can't find resource index programmatically.
+ */
+#define EMPIRIC_MANIFEST_RESOURCE_INDEX 2
+
+
 static HMODULE gtk_dll;
 
 BOOL WINAPI
@@ -49,6 +56,89 @@ DllMain (HINSTANCE hinstDLL,
   return TRUE;
 }
 
+static BOOL CALLBACK
+find_first_manifest (HMODULE  module_handle,
+                     LPCSTR   resource_type,
+                     LPSTR    resource_name,
+                     LONG_PTR user_data)
+{
+  LPSTR *result_name = (LPSTR *) user_data;
+
+  if (resource_type == RT_MANIFEST)
+    {
+      if (IS_INTRESOURCE (resource_name))
+        *result_name = resource_name;
+      else
+        *result_name = g_strdup (resource_name);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+/**
+ * Grabs the first manifest it finds in libgtk3 (which is expected to be the
+ * common-controls-6.0.0.0 manifest we embedded to enable visual styles),
+ * uses it to create a process-default activation context, activates that
+ * context, loads up the library passed in @dllname, then deactivates and
+ * releases the context.
+ *
+ * In practice this is used to force system DLLs (like comdlg32) to be
+ * loaded as if the application had the same manifest as libgtk3
+ * (otherwise libgtk3 manifest only affests libgtk3 itself).
+ * This way application does not need to have a manifest or to link
+ * against comctl32.
+ *
+ * Note that loaded library handle leaks, so only use this function in
+ * g_once_init_enter (leaking once is OK, Windows will clean up after us).
+ */
+void
+_gtk_load_dll_with_libgtk3_manifest (const gchar *dll_name)
+{
+  HANDLE activation_ctx_handle;
+  ACTCTXA activation_ctx_descriptor;
+  ULONG_PTR activation_cookie;
+  LPSTR resource_name;
+  BOOL activated;
+
+  resource_name = NULL;
+  EnumResourceNames (gtk_dll, RT_MANIFEST, find_first_manifest,
+                     (LONG_PTR) &resource_name);
+
+  if (resource_name == NULL)
+    resource_name = MAKEINTRESOURCEA (EMPIRIC_MANIFEST_RESOURCE_INDEX);
+
+  memset (&activation_ctx_descriptor, 0, sizeof (activation_ctx_descriptor));
+  activation_ctx_descriptor.cbSize = sizeof (activation_ctx_descriptor);
+  activation_ctx_descriptor.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID |
+                                      ACTCTX_FLAG_HMODULE_VALID |
+                                      ACTCTX_FLAG_SET_PROCESS_DEFAULT;
+  activation_ctx_descriptor.hModule = gtk_dll;
+  activation_ctx_descriptor.lpResourceName = resource_name;
+  activation_ctx_handle = CreateActCtx (&activation_ctx_descriptor);
+
+  if (activation_ctx_handle == INVALID_HANDLE_VALUE)
+    g_warning ("Failed to CreateActCtx for module %p, resource %p: %lu\n",
+               gtk_dll, resource_name, GetLastError ());
+  else
+    {
+      activation_cookie = 0;
+      activated = ActivateActCtx (activation_ctx_handle, &activation_cookie);
+
+      if (!activated)
+        g_warning ("Failed to ActivateActCtx: %lu\n", GetLastError ());
+
+      LoadLibraryA (dll_name);
+
+      if (activated && !DeactivateActCtx (0, activation_cookie))
+        g_warning ("Failed to DeactivateActCtx: %lu\n", GetLastError ());
+
+      ReleaseActCtx (activation_ctx_handle);
+    }
+
+  if (!IS_INTRESOURCE (resource_name))
+    g_free (resource_name);
+}
+
 const gchar *
 _gtk_get_libdir (void)
 {
diff --git a/gtk/libgtk3.manifest.in b/gtk/libgtk3.manifest.in
new file mode 100644 (file)
index 0000000..6e1677e
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity
+      version="1.0.0.0"
+      processorArchitecture="@EXE_MANIFEST_ARCHITECTURE@"
+      name="libgtk3"
+      type="win32"
+  />
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+          type="win32"
+          name="Microsoft.Windows.Common-Controls"
+          version="6.0.0.0"
+          processorArchitecture="@EXE_MANIFEST_ARCHITECTURE@"
+          publicKeyToken="6595b64144ccf1df"
+          language="*"
+      />
+    </dependentAssembly>
+  </dependency>
+</assembly>